import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.ListIterator;

import processing.core.*;


/**
 * @author Tiina, Chris, Lauri
 * 
 * Luokka kuvaa graafista käyttöliittymää ja siinä tapahtuvia asioita.
 */

public class UserInterface extends PApplet {
	private static final long serialVersionUID = 1L;
	private Clock clock;
	private PImage map;
	private PImage stamp;
	private PImage postcard;
	private Calendar calendar;
	private String weekday;
	private String date;
	private final String[] daysOfWeek = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
	private int hoursSet; // tunti, jolla voi valita, mistä eteenpäin lentoja halutaan katsoa
	private int dayIndex; //indeksi daysOfWeek-taulukossa
	private int multipleCross; //kuinka monta kertaa vuorokausiraja ylitetään klikkailemalla + tai -
	private boolean thisDay; //ollaanko vuorokausirajan tällä puolella
	private boolean terminaattori; // if (true) -> tuhotaan kaikki ilmassa olevat lennot
	private boolean postcardClicked;
	private boolean today; //ollaan tämän vuorokauden puolella; eri asia kuin thisDay
	private boolean todayPrevious; //today-muuttujan edellinen arvo
	private boolean introssa; //ollaanko postikorttinakymassa
	private boolean lastUpdate; //muuttiko edellinen "ajassa siirtyminen" päivämäärää ja viikonpäivää eteen vai taakse
	private PFont font;
	private SimpleDateFormat sdf;
	public Reitti lightOn;
	public Reitti drawThisDestination;
	private ArrayList<Lento> timeTable;
	private ArrayList<Reitti> ilmassa;
	private ArrayList<Lento> feedList;
	private Valo pimeys;
	private int speed; // kellon nopeus



	public void setup() {
		System.out.println("initializing.");
		size(1250, 650);

		this.map = loadImage("map4.jpg");
		this.stamp = loadImage("stamp.png");
		this.postcard = loadImage("postcard.jpg");
		this.calendar = Calendar.getInstance();
		this.font = createFont("Cambria", 22);
		this.dayIndex = 0;
		this.weekday = this.setWeekDay(); //Haetaan viikonpäivä
		this.thisDay = true;
		this.sdf = new SimpleDateFormat("dd.MM.yyyy");
		this.date = day() + "." + month() + "." + year();
		this.today = true;
		this.todayPrevious = true;
		this.multipleCross = 0;
		this.speed =5;

		try {
			this.calendar.setTime(sdf.parse(this.date));
		} catch (ParseException e) {
			e.printStackTrace();
		}

		this.pimeys = new Valo(this);

		//Haetaan seuraavan 24 tunnin aikataulut
		WebLukija web = new WebLukija();
		this.timeTable = web.annaAikataulu();
		this.ilmassa = new ArrayList<Reitti>();
		System.out.println("Have a nice trip!");

		this.feedList = new ArrayList<Lento>();
		this.introssa = true;
	}


	/*Asetetaan viikonpäiväteksti sen perusteella, mikä päivämäärä on*/
	private String setWeekDay() {
		int weekday = calendar.get(Calendar.DAY_OF_WEEK);
		String day;

		switch(weekday){
		case 1: day = "Sunday"; this.dayIndex = 0; break;
		case 2: day = "Monday"; this.dayIndex = 1; break;
		case 3: day = "Tuesday"; this.dayIndex = 2; break;
		case 4: day = "Wednesday"; this.dayIndex = 3; break;
		case 5: day = "Thursday"; this.dayIndex = 4; break;
		case 6: day = "Friday"; this.dayIndex = 5; break;
		case 7: day = "Saturday"; this.dayIndex = 6; break;
		default: day = "default";
		}
		return day;
	}


	/*Päivitetään viikonpäivää. Jos whichWay on true, muutetaan eteenpäin, muuten taaksepäin.*/
	public void updateWeekDay(boolean whichWay) {
		if (whichWay) {
			if (this.dayIndex == 6) {
				this.dayIndex = 0;
			}
			else {
				this.dayIndex++;
			}
			this.calendar.add(Calendar.DATE, 1);
			this.date = this.sdf.format(this.calendar.getTime());
		}
		else {
			if (this.dayIndex == 0) {
				this.dayIndex = 6;
			}
			else {
				this.dayIndex--;
			}
			this.calendar.add(Calendar.DATE, -1);
			this.date = this.sdf.format(this.calendar.getTime());
		}
		this.weekday = this.daysOfWeek[this.dayIndex];
	}


	public void draw() {
		this.piirraTausta();

		if (!this.postcardClicked) {
			this.drawGMT();
			image(this.map, 25, 25);
			image(this.stamp, 1048, 448); //Piirretään leima

			//Piirretään sumennus alkuun
			fill(249, 244, 219, 150);
			rect(0, 0, 1250, 650);

			imageMode(CORNER);
			image(this.postcard, 340, 60);

		}

		else {
			this.clock.display(); //Käsketään kelloa piirtämään itsensä
			this.drawGMT();

			//Piirretään kartta valoisuusvyöhykkeineen
			pimeys.draw();

			this.drawValoYmpyra(); //Piirretään valoheijaste koneen alapuolelle
			this.flightCheck();

			imageMode(CORNER);
			image(this.stamp, 1048, 448);

			this.drawFeed();

			// Piirretään puhekupla lentojen päälle, jos hiiri on koneen päällä
			this.drawPuhekuplaMaaranpaa();
		}
	}


	/**
	 * Piiretään aikavyöhykkeet
	 */
	private void drawGMT() {
		for(int i=0; i<15; i++)
		{
			float kohta = 95+i*1400/18;
			fill(120);
			stroke(120);
			textSize(15);
			textAlign(CENTER);
			line(kohta, 20, kohta, 25);
			text(i-5, kohta, 17); //7, 16

		}
		text("GMT", 44, 20);	
	}

	/**
	 * Jos hiiri on lentokoneen yläpuolella, piirretään valokehä koneiden alaupolelle valaisemaan maata
	 */
	private void drawValoYmpyra() {
		if (this.lightOn == null) { 
			// Hiiri ei ole koneen yläpuolella, eikä tarvitse piirtää valoympyrää lentokoneen alle
			return;
		}		
		loadPixels();
		this.lightOn.valaise();
		updatePixels();
	}

	/**
	 * Jos hiiri on lentokoneen yläpuolella, piirretään puhekupla lentokoneen viereen, josta selviää määränpää
	 */
	private void drawPuhekuplaMaaranpaa() {
		if (this.drawThisDestination == null) { 
			// Hiiri ei ole koneen yläpuolella, eikä tarvitse piirtää puhekuplaa lentokoneen viereen
			return;
		}		
		fill(0);

		String kaupunki = this.drawThisDestination.getLentotiedot().giveDestination();
		float rWid = kaupunki.length()*8+15; // kuplan leveys
		float rHei = 24; // kuplan korkeus
		float x = this.drawThisDestination.currentX-rWid-15; // vasen yläkulma x-koordinaatti
		float y = this.drawThisDestination.currentY+15;		 // vasen yläkulma y

		triangle(x+rWid-15 , y+5, x+rWid-5, y+15, x+rWid+10, y-10); // kolmio puhekupla-lentokone välille

		float corner = rWid/(float)10.0;
		float midDisp = rWid/(float)20.0;

		// Piirretään kupla pyöreillä reunoilla
		beginShape();  
		curveVertex(x+corner,y);
		curveVertex(x+rWid-corner,y);
		curveVertex(x+rWid+midDisp,y+rHei/(float)2.0);
		curveVertex(x+rWid-corner,y+rHei);
		curveVertex(x+corner,y+rHei);
		curveVertex(x-midDisp,y+rHei/(float)2.0);		  
		curveVertex(x+corner,y);
		curveVertex(x+rWid-corner,y);
		curveVertex(x+rWid+midDisp,y+rHei/(float)2.0);
		endShape();

		fill(255);
		// Piirretään kaupunkiteksti
		textSize(12);
		text(kaupunki,x+11,y+16);
	}

	/**
	 * Tarkistetaan kellonaika ja vertaillaan sitä flightSchedulesta löytyvien lentojen lähtöaikojen kanssa.
	 * Jos aika on sopiva, luodaan uusia lentoja.
	 * 
	 * @author chriisu
	 */
	private void flightCheck() {
		int time = this.clock.getMinutes();
		Lento l;
		this.feedList.clear(); // tyhjennetään lähtevien lentojen lista
		boolean epatoivo = false;
		int ekalento = this.timeTable.get(0).giveDepartureMinutes();
		while (this.feedList.size() != 10) {
			ListIterator<Lento> itra = this.timeTable.listIterator();
			while (itra.hasNext()) {
				l = itra.next();

				int m = l.giveDepartureMinutes(); //Lähtöaika
				if (time <= m  )  {
					if (time == m) { //On lähdön aika
						this.lennaLentoon(l); //Laitetaan lento matkaan
					}
					if (this.feedList.size() < 10 && ( time > ekalento || m < ekalento)) {
						this.feedList.add(l);
					}
				}
				else if (time - m < 6 && !l.midAir()) {
					this.lennaLentoon(l); //Laitetaan lento matkaan
				}

				if (epatoivo) { 
					if (this.feedList.size() < 10 && m < this.timeTable.get(0).giveDepartureMinutes() ) {
						this.feedList.add(l);
					}
				}
				else {
					this.piirraPallo(l);
				}


			}
			epatoivo = true; 
			/* Jos ekalla loopilla ei saatu feedList-taulukkoa täyteen,
			 * toisella kerralla feedList taulukkoon lisätään väljemmillä ehdoilla 
			 * lentoja.
			 */
		}


		if (this.terminaattori) { //Tuhotaan kaikki ilmassa olevat lennot
			this.blanko();
			this.terminaattori = false;
		}
		else {
			this.lentojenTilanPaivitys(); //Päivitetään lentojen tila
		}
	}

	
	private void lennaLentoon(Lento l) {
		Reitti uusiReitti = new Reitti(l.giveDestX(), l.giveDestY(), l.giveFlightTime(), this, l);
		ilmassa.add(uusiReitti); //Lisätään Reitti päivitettävien reittien listaan
		l.setMidAir(true); //Kerrotaan Lentotiedoille, että lento lähti jo

	}

	
	/*Piirretään kartalle pallo jokaista kaupunkia kohtaan, jonne 24 tunnin sisällä lähtee lento.*/
	private void piirraPallo(Lento l) {
		noStroke();

		if (l.getSpecial()) { // Varmistetaan, ettei palloa ole jo piirretty
			fill(255);
			ellipse(l.giveDestX(), l.giveDestY(), 3, 3);				
		}

		// Jos lento on käynnissä, piirretään punainen pallura määränpään koordinaatteihin
		if (l.midAir()) {
			fill(200, 50, 50);
			ellipse(l.giveDestX(), l.giveDestY(), 4, 4);
		}

		// Jos hiiri on palluran yläpuolella, kerrotaan pallura-määränpään kaupunki
		if (dist(mouseX, mouseY, l.giveDestX(), l.giveDestY()) < 6) {
			fill(255);
			textSize(12);
			text(l.giveDestination(), l.giveDestX(), l.giveDestY()-8);
		}
	}

	
	/*Päivitetään lentojen tilaa.*/
	private void lentojenTilanPaivitys() {

		//Päivitetään ilmassa olevat lennot
		ListIterator<Reitti> itr = ilmassa.listIterator();
		ArrayList<Reitti> poistettavat = new ArrayList<Reitti>();
		while (itr.hasNext()) {
			Reitti r = itr.next();
			r.draw(); //Piirretään kyseinen lento

			if (r.loppu) { //Pidetään listaa laskeutuneista lennoista
				poistettavat.add(r);
			}
		}

		//Poistetaan laskeutuneet lennot
		for (int j = 0; j < poistettavat.size(); j++ ) {
			Reitti r = poistettavat.get(j);
			ilmassa.remove(r);
		}
	}

	
	/*Piirretään ohjelmassa taustalla olevat nappulat ynnä muut.*/
	private void piirraTausta() {

		background(60, 32, 4);
		smooth();
		fill(255);

		textSize(22);
		textFont(this.font);

		//Päivämäärä ja viikonpäivä
		textAlign(CENTER);
		text(this.weekday, 165, 580);
		text(this.date, 165, 605);

		//"Paneeli", josta voi muuttaa aikaa, josta eteenpäin lentoja näytetään
		text("Starting at", 380, 575);
		noStroke();
		rect(335, 585, 42, 30); //tuntien suorakulmio
		rect(397, 585, 29, 30); //play-merkin suorakulmio
		fill(170);
		rect(380, 585, 14, 14); //plus-merkin suorakulmio
		rect(380, 601, 14, 14); //miinus-merkin suorakulmio

		fill(0);
		textSize(27);
		text(Integer.toString(this.hoursSet), 356, 609); //tunnit
		textSize(20);
		text("+", 387, 598);
		text("-", 387, 614);
		triangle(408, 590, 419, 600, 408, 610); //play-merkki
		fill(255);

		
		// Nopeusteksti
		textSize(16);
		text("Speed:", 540, 622);
		// Nopeus-säätimet
		fill(170);
		rect(575, 610, 15, 15); // miinus
		rect(600, 610, 15, 15); // plus
		
		textSize(20);
		fill(0);
		text("-", 583, 624);
		text("+", 608, 624);
		fill(255);
	}

	/*Palautetaan minuutit.*/
	public int annaMin() {
		return this.clock.getMinutes();
	}
	
	private void speedup(boolean up) {
		this.blanko();
		if (up) {
			this.speed += 5;
		}
		else {
			this.speed -= 5;
		}
		if (this.speed < 1) {
			this.speed = 1;
		}
		int hours = this.clock.getHoursReal();
		this.clock = new Clock(36, 570, 595, this, this.speed);
		this.clock.setTime(hours, 0);
	}

	
	/*Piirretään feed, jossa näkyvät tulevat lennot.*/
	public void drawFeed() {
		textSize(15);
		fill(0);
		rect(675,535,350,100);
		textAlign(LEFT);
		fill(255,255,0);
		
		//Asetetaan tiedot oikeisiin paikkoihin.
		for (int i = 0 ; i < 5 && i < this.feedList.size() ; i++)  {
			text(this.feedList.get(i).giveDepartureString(), 700, 560+i*15);
			text(this.feedList.get(i).giveDestination(), 750, 560+i*15);
		}

		for (int i = 5 ; i < this.feedList.size() ; i++) { 	
			text(this.feedList.get(i).giveDepartureString(), 865, 560+(i-5)*15);
			text(this.feedList.get(i).giveDestination(), 915, 560+(i-5)*15);
		}
	}



	/**
	 * Muutetaan tunteja boksissa, josta play-nappia painamalla saa kellonaikaa siirrettyä.
	 * @param whichWay kertoo lisätäänkö tunteja (true => lisäys, false => väehnnys)
	 */
	public void changeHours(boolean whichWay) {
		//hoursSet on potentiaalinen tuleva aika (boksin sisällä)
		if (!whichWay && this.hoursSet != this.clock.giveHours()) {
			if (this.hoursSet == 0) {
				this.hoursSet = 23;
				this.todayPrevious = this.today;
				this.today = true;
				this.multipleCross++;
			}
			else {
				this.hoursSet--;
			}
		}
		else if (whichWay) {
			if (this.hoursSet == 23) {
				this.hoursSet = 0;
				this.todayPrevious = this.today;
				this.today = false;
				this.multipleCross++;
			}
			else {
				this.hoursSet++;
			}
		}
	}

	/*Hiiren klikkauksesta aiheutuvat toiminnot*/
	public void mouseClicked() {
		// PLUSSA-NAPPI
		if (mouseX >= 380 && mouseX <= 394 && mouseY >= 585 && mouseY <= 598 && this.thisDay) {
			this.changeHours(true);

			if (this.hoursSet == this.clock.giveHours()-1) {
				this.thisDay = false;
			}
		}

		// MIINUS-NAPPI
		else if (mouseX >= 380 && mouseX <= 394 && mouseY >= 601 && mouseY <= 615 && !(this.hoursSet == this.clock.giveHours())) {

			if (this.hoursSet == this.clock.giveHours()-1) {
				this.thisDay = true;
			}

			this.changeHours(false);
		}

		// OK-NAPPI
		else if (mouseX >= 397 && mouseX <= 426 && mouseY >= 585 && mouseY <= 615) {
			this.clock.setTime(this.hoursSet, 0);
			this.terminaattori = true;
			
			if (!this.today && this.todayPrevious && this.multipleCross % 2 == 1 && this.multipleCross != 0 && !this.lastUpdate) {
				this.updateWeekDay(true);
				this.lastUpdate = true;
				this.multipleCross = 0;
			}
			else if(this.today && !this.todayPrevious && this.multipleCross != 0 && this.lastUpdate) {
				this.updateWeekDay(false);
				this.lastUpdate = false;
				this.multipleCross = 0;
			}
		}

		// POSTIKORTTI
		else if (mouseX >= 250 && mouseX <= 820 && mouseY >= 100 && mouseY <= 535 && introssa) {
			this.postcardClicked = true;

			this.clock = new Clock(36, 570, 595, this, 5);
			this.clock.initTime();
			this.hoursSet = this.clock.giveHours();
			introssa = false;
		}
		
		else if (mouseX >=575 && mouseX <= 590 && mouseY >= 610 && mouseY <= 625) {
			this.speedup(false);
		}
		else if (mouseX >=600 && mouseX <= 615 && mouseY >= 610 && mouseY <= 625) {
			this.speedup(true);
		}
	}

	// Kerrotaan kellonajan nopeuskerroin
	public int annaNopeuskerroin() {
		return this.clock.annaNopeuskerroin();		
	}


	/**
	 * Metodi alustaa lentojen tilan, eli kaikki käynnissä olevat lennot tuhotaan!
	 */
	private void blanko() {
		Reitti r;
		//Poistetaan
		for (int j = 0; j < ilmassa.size(); j++ ) {
			r = ilmassa.get(j);
			r.getLentotiedot().setMidAir(false); // Kerrotaan lentotiedoille, että kyseinen lento ei enää lennä
		}
		this.ilmassa.clear();
	}


	public static void main(String args[]) {
		System.out.println(System.getProperty("java.class.path"));
		PApplet.main(new String[] { "--present", "UserInterface" });
	}

}
